package com.boss.core.form;

import com.boss.Messages;
import com.boss.core.BossConstants;
import com.boss.core.BossResponse;
import com.boss.core.common.Artifact;
import com.boss.core.common.Target;
import com.boss.core.common.TargetLinuxBox;
import com.boss.core.manage.BossProperty;
import com.boss.core.manage.PropertySection;
import com.boss.core.util.BossGlobalUtil;
import com.boss.core.util.BossUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;

import javax.servlet.ServletException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Helper class for Boss forms
 *
 * @author Momin Noor Khan
 */
public class BossFormHelper {
    /**
     * Performs the HTTP POST on the given URL and uses the response code to set the status messages to be displayed
     * on the post-submit landing page.
     *
     * @param req
     * @param jobURL
     * @throws javax.servlet.ServletException
     * @throws java.io.IOException
     */
    public static void postToUrl(StaplerRequest req, String jobUrl) throws ServletException, IOException {
        BossResponse bossResponse = BossUtil.postToURL(jobUrl);
        int ret = bossResponse.getResponseCode();
        String exceptionMsg = bossResponse.getExceptionMsg();
        String stackTraceElement = bossResponse.getStackTraceElement();

        // Set the values of post-submit landing page's display properties
        if (ret == HttpURLConnection.HTTP_OK) {
            setSuccessMsg(req);
        } else if (ret == HttpURLConnection.HTTP_INTERNAL_ERROR) {
            setFailureMsg(req, Messages.Boss_ErrorInternalServer());
            //[To-Do: retrieve info from stack trace]
            req.setAttribute(BossConstants.SUBMIT_EXCEPTION, exceptionMsg);
            req.setAttribute(BossConstants.STACKTRACE_ELEMENT, stackTraceElement);
        }
        /* ideally this FORBIDDEN should not be allowed as 'success', but in our case we are facing the problem
        that probably HTTPUrlConnection is not performing pre-emptive BASIC authentication but still it is allowing the POST.
        [To-Do : Try HttpClient from Apache or Bouncycastle] */
        else if (ret == HttpURLConnection.HTTP_FORBIDDEN) {
            setSuccessMsg(req);
        } else {
            setFailureMsg(req, Messages.Boss_ErrorUnknown());
            req.setAttribute(BossConstants.SUBMIT_EXCEPTION, exceptionMsg);
            req.setAttribute(BossConstants.STACKTRACE_ELEMENT, stackTraceElement);
        }
    }

    /**
     * Validation method to check project
     *
     * @param req
     * @param project
     * @return
     */
    public static boolean checkProject(StaplerRequest req, String project) {
        if (StringUtils.isBlank(project) || project.equals(BossConstants.DUMMY_VALUE)) {
            setValidationErrorMsg(req, Messages.Boss_MandatoryProject());
            return false;
        }
        return true;
    }

    /**
     * Validation method to check release
     *
     * @param req
     * @param release
     * @return
     */
    public static boolean checkRelease(StaplerRequest req, String release) {
        if (StringUtils.isBlank(release) || release.equals(BossConstants.DUMMY_VALUE)) {
            setValidationErrorMsg(req, Messages.Boss_MandatoryRelease());
            return false;
        }
        return true;
    }

    /**
     * Validation method to check task
     *
     * @param req
     * @param task
     * @return
     */
    public static boolean checkTask(StaplerRequest req, String task) {
        if (StringUtils.isBlank(task) || task.equals(BossConstants.DUMMY_VALUE)) {
            setValidationErrorMsg(req, Messages.Boss_MandatoryTask());
            return false;
        }
        return true;
    }

    /**
     * Check available job
     *
     * @param req
     * @param availableJob
     * @return
     */
    public static boolean checkAvailableJob(StaplerRequest req, String availableJob) {
        boolean valid = true;
        if (!StringUtils.isBlank(availableJob) && availableJob.equals(BossConstants.JOB_NOT_AVAILABLE)) {
            setFailureMsg(req, Messages.Boss_JobUnAvailable());
            valid = false;
        } else if (!StringUtils.isBlank(availableJob) && availableJob.equals(BossConstants.JOB_INVALID)) {
            setFailureMsg(req, Messages.Boss_JobInvalid());
            valid = false;
        }
        return valid;
    }

    /**
     * Set the validation error message to be shown on the post-submit landing page
     *
     * @param req
     * @param errMsg
     */
    public static void setValidationErrorMsg(StaplerRequest req, String errMsg) {
        String validationErrorMsg = req.getAttribute(BossConstants.VALIDATION_ERROR_MSG) == null ? "" : req.getAttribute(BossConstants.VALIDATION_ERROR_MSG).toString();
        validationErrorMsg += errMsg + BossConstants.BREAK;
        req.setAttribute(BossConstants.SUBMIT_STATUS, Messages.Boss_SubmitInvalid());
        req.setAttribute(BossConstants.SUBMIT_MESSAGE, Messages.Boss_SubmitInvalidMsg());
        req.setAttribute(BossConstants.VALIDATION_ERROR_MSG, validationErrorMsg);
    }

    /**
     * Set the success message to be shown on the post-submit landing page
     *
     * @param req
     */
    public static void setSuccessMsg(StaplerRequest req) {
        req.setAttribute(BossConstants.SUBMIT_STATUS, Messages.Boss_SubmitSuccess());
        req.setAttribute(BossConstants.SUBMIT_MESSAGE, Messages.Boss_SubmitSuccessMsg());
    }

    /**
     * Set the failure message to be shown on the post-submit landing page
     *
     * @param req
     * @param description
     */
    public static void setFailureMsg(StaplerRequest req, String description) {
        req.setAttribute(BossConstants.SUBMIT_STATUS, Messages.Boss_SubmitFailure());
        req.setAttribute(BossConstants.SUBMIT_MESSAGE, Messages.Boss_SubmitFailureMsg());
        req.setAttribute(BossConstants.SUBMIT_DESCRIPTION, description);
    }

    /**
     * Get the target profile
     *
     * @param hasTargetSection
     * @param req
     * @return
     */
    public static String getTargetProfile(boolean hasTargetSection, StaplerRequest req) {
        String targetProfile = "";
        if (hasTargetSection) {
            try {
                targetProfile = getTargetProfiles(req);
            } catch (ServletException e) {
                e.printStackTrace();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return targetProfile;
    }

    /**
     * Get target profiles
     *
     * @param req
     * @return
     * @throws IOException
     * @throws ServletException
     */
    public static String getTargetProfiles(StaplerRequest req) throws IOException, ServletException {
        return getTargetProfiles(req, BossGlobalUtil.getOc4jProfilesPath());
    }

    /**
     * Get target profiles
     *
     * @param req
     * @param oc4jProfilesPath
     * @return
     * @throws IOException
     * @throws ServletException
     */
    public static String getTargetProfiles(StaplerRequest req, String oc4jProfilesPath) throws IOException, ServletException {
        String retTargetProfile = "";
        List<String> list = new ArrayList<String>();
        List<Target> targetProfiles = new ArrayList<Target>();
        JSONObject jsonForm = req.getSubmittedForm();
        targetProfiles.addAll(req.bindJSONToList(Target.class, jsonForm.getJSONObject("Task").get("target")));

        for (Target target : targetProfiles) {
            if (!BossFormHelper.isBlankOrDummy(target)) {
                String targetContainer = target.getServer() + "." +
                        target.getPort() + "." +
                        target.getEnvironment() +
                        BossConstants.SUFFIX_DOT_PROPERTIES;

                if (!list.contains(targetContainer)) {
                    list.add(targetContainer);
                }
            }
        }

        if (list.size() != 0) {
            for (String targetProfile : list) {
                retTargetProfile = retTargetProfile + oc4jProfilesPath + targetProfile + ";";
            }
            retTargetProfile = StringUtils.removeEnd(retTargetProfile, ";");
        }
        return retTargetProfile;
    }

    /**
     * Get GIS target profile
     *
     * @param hasTargetSection
     * @param req
     * @return
     */
    public static String getGisTargetProfile(boolean hasTargetSection, StaplerRequest req) {
        String targetProfile = "";
        if (hasTargetSection) {
            try {
                targetProfile = getGisTargetProfile(req);
            } catch (ServletException e) {
                e.printStackTrace();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return targetProfile;
    }

    /**
     * Get GIS target profile
     *
     * @param req
     * @return
     * @throws IOException
     * @throws ServletException
     */
    public static String getGisTargetProfile(StaplerRequest req) throws IOException, ServletException {
        String retTargetProfile = "";
        List<String> list = new ArrayList<String>();
        JSONObject jsonForm = req.getSubmittedForm();
        JSONObject jsonTaskSection = BossUtil.getObjectFromJson(jsonForm, BossConstants.KEY_TASK);
        Object jsonTargetSection = jsonTaskSection.get(BossConstants.KEY_TARGET);
        if (jsonTargetSection instanceof JSONArray) {
            list = getTargetListFromJsonArray(jsonTaskSection);
        } else if (jsonTargetSection instanceof JSONObject) {
            list = getTargetListFromJsonObject(req, jsonTaskSection);
        }
        retTargetProfile = getFormattedTargetProfile(list);

        return retTargetProfile;
    }

    /**
     * Get GIS Customers for builds
     *
     * @param req
     * @return
     */
    public static String getGisCustomers(StaplerRequest req) {
        String gisCustomers = "";
        JSONObject jsonForm;
        JSONObject jsonTaskSection;
        Object jsonTargetSection;
        PropertySection propertySection = BossGlobalUtil.getPropertySection(BossConstants.GIS_CUSTOMER_KEY);
        List<BossProperty> customers = propertySection.getProperties();

        try {
            jsonForm = req.getSubmittedForm();
            jsonTaskSection = BossUtil.getObjectFromJson(jsonForm, BossConstants.KEY_TASK);
            jsonTargetSection = jsonTaskSection.get(BossConstants.KEY_TARGET);
            for (BossProperty customer : customers) {
                if (jsonForm.getJSONObject("Task").getJSONObject("target").has(customer.getName())) {
                    if (jsonForm.getJSONObject("Task").getJSONObject("target").getBoolean(customer.getName())) {
                        gisCustomers = gisCustomers + StringUtils.strip(customer.getName()) + ",";
                    }
                }
            }
            gisCustomers = StringUtils.removeEnd(gisCustomers, ",");
        } catch (ServletException e) {
            e.printStackTrace();
        }
        return gisCustomers;
    }

    public static String getSelectedGisCustomers(String task, JSONObject jsonTaskSection) {
        String selectedCustomers = "";
        if (task.equals(BossConstants.TASK_COPY_BUILD)) {
            List<BossProperty> allCustomers = getAllGisCustomers();
            for (BossProperty customer : allCustomers) {
                if (jsonTaskSection != null && jsonTaskSection.has("target")) {
                    JSONObject jsonTargetSection = jsonTaskSection.getJSONObject("target");
                    if (jsonTargetSection != null && jsonTargetSection.has(customer.getName())) {
                        if (jsonTargetSection.getBoolean(customer.getName())) {
                            selectedCustomers = selectedCustomers + StringUtils.strip(customer.getName()) + ",";
                        }
                    }
                }
            }
            selectedCustomers = StringUtils.removeEnd(selectedCustomers, ",");
        }
        return selectedCustomers;
    }

    public static List<BossProperty> getAllGisCustomers() {
        PropertySection propertySection = BossGlobalUtil.getPropertySection(BossConstants.GIS_CUSTOMER_KEY);
        List<BossProperty> properties = propertySection.getProperties();
        return properties;
    }

    /**
     * Get target list from json array
     *
     * @param jsonTaskSection
     * @return
     */
    public static List<String> getTargetListFromJsonArray(JSONObject jsonTaskSection) {
        List<String> list = new ArrayList<String>();

        for (int i = 0; i < jsonTaskSection.getJSONArray(BossConstants.KEY_TARGET).size(); i++) {
            JSONObject jServer = ((JSONObject) jsonTaskSection.getJSONArray(BossConstants.KEY_TARGET).get(i)).getJSONObject("Server");
            list = getDecoratedTargetList(jServer, list);
        }
        return list;
    }

    /**
     * Get target list from the json object
     *
     * @param req
     * @param jsonTaskSection
     * @return
     */
    public static List<String> getTargetListFromJsonObject(StaplerRequest req, JSONObject jsonTaskSection) {
        List<String> list = new ArrayList<String>();
        List<TargetLinuxBox> targetProfiles = new ArrayList<TargetLinuxBox>();
        targetProfiles.addAll(req.bindJSONToList(TargetLinuxBox.class, jsonTaskSection.getJSONObject(BossConstants.KEY_TARGET).get("Server")));

        for (TargetLinuxBox t : targetProfiles) {
            JSONObject jServer = jsonTaskSection.getJSONObject(BossConstants.KEY_TARGET).getJSONObject("Server");
            list = getDecoratedTargetList(jServer, list);
        }
        return list;
    }

    /**
     * Get the decorated target list
     *
     * @param jServer
     * @param list
     * @return
     */
    public static List<String> getDecoratedTargetList(JSONObject jServer, List<String> list) {
        if (!(BossUtil.isBlankOrDummy(jServer.getString(BossConstants.KEY_STAPLER_CLASS))
                ||
                BossUtil.isBlankOrDummy(jServer.getString(BossConstants.KEY_TARGET_LOCATION)))) {
            String target = jServer.getString(BossConstants.KEY_STAPLER_CLASS) +
                    "@" + jServer.getString(BossConstants.KEY_TARGET_LOCATION);

            //add to list if it is not a duplicate
            if (!list.contains(target)) {
                list.add(target);
            }
        }
        return list;
    }

    /**
     * Get the formatted target profile
     *
     * @param list
     * @return
     */
    public static String getFormattedTargetProfile(List<String> list) {
        String formattedTargetProfile = "";
        if (list.size() != 0) {
            for (String targetProfile : list) {
                //delimit subsequent targets by using semi-colon as a prefix.
                formattedTargetProfile = formattedTargetProfile + targetProfile + ";";
            }
            //remove the trailing semi-colon (if any)
            formattedTargetProfile = StringUtils.removeEnd(formattedTargetProfile, ";");
        }
        return formattedTargetProfile;
    }

    /**
     * Get the selected artifacts
     *
     * @param hasArtifactSection
     * @param req
     * @return
     */
    public static String getSelectedArtifacts(boolean hasArtifactSection, StaplerRequest req) {
        String artifacts = "";
        if (hasArtifactSection) {
            try {
                artifacts = getArtifacts(req);
            } catch (ServletException e) {
                e.printStackTrace();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return artifacts;
    }

    /**
     * Gets the artifacts selected by the user on the form from the repeatable 'Artifact' section
     */
    public static String getArtifacts(StaplerRequest req) throws IOException, ServletException {
        String retArtifactInfo = "";
        List<String> list = new ArrayList<String>();
        List<Artifact> artifacts = new ArrayList<Artifact>();
        JSONObject jsonForm = req.getSubmittedForm();
        artifacts.addAll(req.bindJSONToList(Artifact.class, jsonForm.getJSONObject("Task").get("artifact")));

        for (Artifact artifact : artifacts) {
            if (!BossFormHelper.isBlankOrDummy(artifact)) {
                //format is modulename@versionnumber;
                String item = artifact.getModuleName() + "@" + artifact.getVersionNumber() + ";";
                //add to list if it is not a duplicate
                if (!list.contains(item)) {
                    list.add(item);
                }
            }
        }

        if (list.size() != 0) {
            for (String artifact : list) {
                retArtifactInfo = retArtifactInfo + artifact;
            }
            //remove the trailing semi-colon (if any)
            retArtifactInfo = StringUtils.removeEnd(retArtifactInfo, ";");
        }
        return retArtifactInfo;
    }

    /**
     * Get the selected target profile - 2 (used in PAM order form)
     *
     * @param hasTargetSection
     * @param req
     * @return
     */
    public static String getSelectedTargetProfile(boolean hasTargetSection, StaplerRequest req) {
        String targetProfile = "";
        if (hasTargetSection) {
            try {
                targetProfile = getTargetProfiles2(req);
            } catch (ServletException e) {
                e.printStackTrace();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return targetProfile;
    }

    /**
     * Gets the target OC4J profiles selected by the user on the form.
     * On the form one instance of a target corresponds to one target oc4j profile in CCSAUX.
     * It comes in handy when the deploy order is to be carried out on multiple servers from the same build order.
     * Note: Differs with getTargetProfile() in suffix of containers only
     *
     * @param req
     * @throws java.io.IOException
     * @throws javax.servlet.ServletException
     */
    public static String getTargetProfiles2(StaplerRequest req) throws IOException, ServletException {
        String retTargetProfile = "";
        List<String> list = new ArrayList<String>();
        List<Target> targetProfiles = new ArrayList<Target>();
        JSONObject jsonForm = req.getSubmittedForm();
        targetProfiles.addAll(req.bindJSONToList(Target.class, jsonForm.getJSONObject("Task").get("target")));

        for (Target target : targetProfiles) {
            //skip this iteration and do not add the target to the list if environment/server/port contains dummy value
            if (!BossFormHelper.isBlankOrDummy(target)) {
                String targetContainer = target.getServer() + "." +
                        target.getPort() + "." +
                        target.getEnvironment();

                //add to list if it is not a duplicate
                if (!list.contains(targetContainer)) {
                    list.add(targetContainer);
                }
            }
        }

        if (list.size() != 0) {
            for (String targetProfile : list) {
                //delimit subsequent targets by using semi-colon as a prefix. Don't forget to prefix the path to the properties file as well.
                retTargetProfile = retTargetProfile + targetProfile + ";";
            }
            //remove the trailing semi-colon (if any)
            retTargetProfile = StringUtils.removeEnd(retTargetProfile, ";");
        }
        return retTargetProfile;
    }

    /**
     * Get the decorated querystring
     *
     * @param map
     * @return
     */
    public static String getDecoratedQueryString(Map<String, String> map) {
        StringBuilder urlString = new StringBuilder();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            if (!StringUtils.isBlank(value)) {
                urlString.append("&" + key + "=" + value);
            }
        }
        return BossUtil.encodeQueryString(urlString.toString());
    }

    /**
     * Check if the target is blank or dummy
     *
     * @param target
     * @return
     */
    public static boolean isBlankOrDummy(Target target) {
        boolean result = false;
        if (BossUtil.isBlankOrDummy(target.getEnvironment()) ||
                BossUtil.isBlankOrDummy(target.getServer()) ||
                BossUtil.isBlankOrDummy(target.getPort())) {
            result = true;
        }
        return result;
    }

    /**
     * Check if the artifact is blank or has dummy value
     *
     * @param artifact
     * @return
     */
    public static boolean isBlankOrDummy(Artifact artifact) {
        boolean result = false;
        if (StringUtils.isBlank(artifact.getModuleName())
                ||
                StringUtils.isBlank(artifact.getVersionNumber())
                ||
                artifact.getModuleName().equals(BossConstants.DUMMY_VALUE)
                ||
                artifact.getVersionNumber().equals(BossConstants.DUMMY_VALUE)
                ) {
            result = true;
        }
        return result;
    }
}
